﻿using FrameworkCore.Interfaces;

namespace Infrastructure.Services;

/// <summary>
/// 雪花ID生成器服务实现
/// </summary>
public class GenerateIdService : IGenerateIdService
{
    private const long Twepoch = 1288834974657L;
    private const int WorkerIdBits = 5;
    private const int DatacenterIdBits = 5;
    private const int SequenceBits = 12;

    private readonly long _maxWorkerId = -1L ^ (-1L << WorkerIdBits);
    private readonly long _maxDatacenterId = -1L ^ (-1L << DatacenterIdBits);

    private const int WorkerIdShift = SequenceBits;
    private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
    private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
    private const long SequenceMask = -1L ^ (-1L << SequenceBits);

    private long _lastTimestamp = -1L;
    private long _sequence = 0;

    private readonly object _lock = new object();

    private readonly long _workerId;
    private readonly long _datacenterId;

    private readonly bool _isSnowFlakeId;

    private static readonly DateTime BaseDate = new DateTime(1900, 1, 1);


    /// <summary>
    /// 初始化雪花ID生成器服务
    /// </summary>
    /// <param name="workerId">工作机器ID</param>
    /// <param name="datacenterId">数据中心ID</param>
    /// <param name="isSnowFlakeId">生成雪花ID还是GUID</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public GenerateIdService(long workerId, long datacenterId, bool isSnowFlakeId)
    {
        if (workerId > _maxWorkerId || workerId < 0)
            throw new ArgumentOutOfRangeException(nameof(workerId), $"Worker ID must be between 0 and {_maxWorkerId}");

        if (datacenterId > _maxDatacenterId || datacenterId < 0)
            throw new ArgumentOutOfRangeException(nameof(datacenterId), $"Datacenter ID must be between 0 and {_maxDatacenterId}");

        _workerId = workerId;
        _datacenterId = datacenterId;
        _isSnowFlakeId = isSnowFlakeId;
    }

    /// <summary>
    /// 生成唯一的ID
    /// </summary>
    /// <returns>雪花ID</returns>
    public string GenerateId()
    {
        if (_isSnowFlakeId) return GenerateSnowFlakeId().ToString();
        return GenerateSequentialGuid().ToString();
    }

    /// <summary>
    /// 生成唯一的字符串ID
    /// </summary>
    /// <returns>ID</returns>
    public string GenerateIdAsString() 
    {
        if (_isSnowFlakeId) return GenerateSnowFlakeId().ToString();
        return GenerateSequentialGuid().ToString();
    }

    /// <summary>
    /// 生成唯一的长整型ID
    /// </summary>
    /// <returns>ID</returns>
    public long GenerateIdAsLong() 
    {
        return GenerateSnowFlakeId();
    }

    private long GenerateSnowFlakeId()
    {
        lock (_lock)
        {
            long timestamp = GetCurrentTimestamp();

            if (timestamp < _lastTimestamp)
                throw new InvalidOperationException("Invalid system clock!");

            if (_lastTimestamp == timestamp)
            {
                _sequence = (_sequence + 1) & SequenceMask;
                if (_sequence == 0)
                    timestamp = WaitNextTimestamp(_lastTimestamp);
            }
            else
            {
                _sequence = 0;
            }

            _lastTimestamp = timestamp;

            return ((timestamp - Twepoch) << TimestampLeftShift)
                   | (_datacenterId << DatacenterIdShift)
                   | (_workerId << WorkerIdShift)
                   | _sequence;
        }
    }

    private Guid GenerateSequentialGuid()
    {
        byte[] guidArray = Guid.NewGuid().ToByteArray();
        DateTime now = DateTime.Now;
        TimeSpan days = new TimeSpan(now.Ticks - BaseDate.Ticks);
        TimeSpan msecs = now.TimeOfDay;

        // 将时间信息转换为字节数组
        byte[] daysArray = BitConverter.GetBytes(days.Days);
        byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333));

        // 确保字节序为统一的顺序
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(daysArray);
            Array.Reverse(msecsArray);
        }

        // 将时间信息放入 GUID 的字节数组中
        Array.Copy(daysArray, 0, guidArray, 0, 4);
        Array.Copy(msecsArray, 0, guidArray, 4, 2); // 注意这里改为了 2，因为我们只取 16 位的毫秒数

        // 设置 GUID 的版本号和变体号，确保符合标准
        guidArray[7] = (byte)((guidArray[7] & 0x0F) | (4 << 4)); // 设置 GUID 的版本号
        guidArray[8] = (byte)((guidArray[8] & 0x3F) | 0x80); // 设置 GUID 的变体号

        return new Guid(guidArray);
    }

    private long GetCurrentTimestamp()
    {
        return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
    }

    private long WaitNextTimestamp(long lastTimestamp)
    {
        long timestamp = GetCurrentTimestamp();
        while (timestamp <= lastTimestamp)
        {
            timestamp = GetCurrentTimestamp();
        }
        return timestamp;
    }
}
